package com.jiaz.algorithm;

import com.jiaz.dto.PostionScoreDto;
import com.jiaz.mapper.CheckInMapper;
import com.jiaz.pojo.UserCheckInFreq;
import com.jiaz.untils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author jiajiazi
 * @version 1.0
 * @date 2022/5/12 19:42
 */
@Service
@Slf4j
public class PrecisionRecall {
    @Autowired
    private CheckInMapper checkInMapper;

    @Autowired
    private UserAlgorithm userAlgorithm;

    @Autowired
    private ItemAlgorithm itemAlgorithm;

    private final int[] topk = {5, 10, 15};

    List<UserCheckInFreq> userLastPostions;

    int userId = -1;
    int venueId = -1;
    PostionScoreDto[] userWPR;
    PostionScoreDto[] itemWPR;
    PostionScoreDto[][] WPRCache;


    public double[][][] UserPrecisionRecall() {
        Random random = new Random();
        // precison and recall
        // 算法 top  p/r
        double[][][] res = new double[3][3][2];
        if (userLastPostions == null) {
            userLastPostions = checkInMapper.getUser_Last_Postions();
            for (UserCheckInFreq userLastPostion : userLastPostions) {
                Arrays.sort(userLastPostion.getCheckInpost());
            }
        }
        int dt = random.nextInt(10);

        for (int i = dt, time = 0; i < userLastPostions.size() && time < 100; i += 10, time++) {
            log.info("执行--{}%", (time + 1));
            UserCheckInFreq userCheckInFreq = userLastPostions.get(i);
            PostionScoreDto[][] postionScoreDtos = new PostionScoreDto[3][];
            postionScoreDtos[0] = userAlgorithm.originAlgorithmScore(userCheckInFreq.getUserId(), 0, topk[2]);
            postionScoreDtos[1] = userAlgorithm.freqImproveAlgorithmScore(userCheckInFreq.getUserId(), 0, topk[2]);
            postionScoreDtos[2] = userAlgorithm.PG_gorithmScore(userCheckInFreq.getUserId(), 0, topk[2]);
            for (int k = 0; k < 3; k++) {
                Arrays.sort(postionScoreDtos[k], new Comparator<PostionScoreDto>() {
                    @Override
                    public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                        return o1.getVenueId() - o2.getVenueId();
                    }
                });
                for (int j = 0, pti = 10; j < 3; j++, pti -= 5) {
                    int ptj = 0, commem = 0;
                    while (ptj < userCheckInFreq.getCheckInpost().length && pti < 15) {
                        int cd = postionScoreDtos[k][pti].getVenueId() - userCheckInFreq.getCheckInpost()[ptj];
                        if (cd == 0) {
                            commem++;
                            pti++;
                            ptj++;
                        } else if (cd > 0) {
                            ptj++;
                        } else {
                            pti++;
                        }
                    }
                    res[k][j][0] += commem / (double) topk[j];
                    res[k][j][1] += commem / (double) userCheckInFreq.getCheckInpost().length;
                }
            }
        }
        for (int k = 0; k < 3; k++) {
            for (int j = 0; j < 3; j++) {
                res[k][j][0] /= 100.0;
                res[k][j][1] /= 100.0;
            }
        }
        return res;
    }

    public double[][][] ItemPrecisionRecall() {
        Random random = new Random();
        // precison and recall
        // top 算法 p/r
        double[][][] res = new double[3][2][2];
        if (userLastPostions == null) {
            userLastPostions = checkInMapper.getUser_Last_Postions();
            for (UserCheckInFreq userLastPostion : userLastPostions) {
                Arrays.sort(userLastPostion.getCheckInpost());
            }
        }
        int dt = random.nextInt(10);

        PostionScoreDto[][] postionScoreDtos = new PostionScoreDto[2][];
        postionScoreDtos[0] = itemAlgorithm.PG_gorithmScore(0, 7432, topk[2]);
        Arrays.sort(postionScoreDtos[0], new Comparator<PostionScoreDto>() {
            @Override
            public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                return o1.getVenueId() - o2.getVenueId();
            }
        });
        for (int i = dt, time = 0; i < userLastPostions.size() && time < 100; i += 10, time++) {
            log.info("执行--{}%", (time + 1));
            UserCheckInFreq userCheckInFreq = userLastPostions.get(i);
            postionScoreDtos[1] = itemAlgorithm.originAlgorithmScore(userCheckInFreq.getUserId(), 2256, topk[2]);
            Arrays.sort(postionScoreDtos[1], new Comparator<PostionScoreDto>() {
                @Override
                public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                    return o1.getVenueId() - o2.getVenueId();
                }
            });
            for (int k = 0, jg = 10; k < 3; k++, jg -= 5) {
                for (int h = 0; h < 2; h++) {
                    int ptj = 0;
                    int pti = jg, commen = 0;
                    while (pti < postionScoreDtos[h].length && ptj < userCheckInFreq.getCheckInpost().length) {
                        int d = postionScoreDtos[h][pti].getVenueId() - userCheckInFreq.getCheckInpost()[ptj];
                        if (d == 0) {
                            pti++;
                            ptj++;
                            commen++;
                        } else if (d > 0) {
                            ptj++;
                        } else {
                            pti++;
                        }
                    }
                    res[k][h][0] += commen / (double) topk[k];
                    res[k][h][1] += commen / (double) userCheckInFreq.getCheckInpost().length;
                }
            }
        }
        for (int k = 0; k < 3; k++) {
            for (int j = 0; j < 2; j++) {
                res[k][j][0] /= 100.0;
                res[k][j][1] /= 100.0;
            }
        }
        return res;
    }

    public double[][][] FinalPrecisionRecall() {
        double[][][] res = new double[9][3][2];
        if (userLastPostions == null) {
            userLastPostions = checkInMapper.getUser_Last_Postions();
            for (UserCheckInFreq userLastPostion : userLastPostions) {
                Arrays.sort(userLastPostion.getCheckInpost());
            }
        }
        WPRCache = new PostionScoreDto[2000][];
        Random random = new Random();
        int dt = random.nextInt(10);

        for (int i = 0; i < 9; i++) {
            for (int j = dt, time = 0; j < userLastPostions.size() && time < 100; j += 10, time++) {
                log.info("执行 a={} == {}%", (i + 1) / 10.0, time + 1);
                UserCheckInFreq userCheckInFreq = userLastPostions.get(j);
                for (int k = 0; k < topk.length; k++) {
                    PostionScoreDto[] postionScoreDtos =  getWPRScore(userCheckInFreq.getUserId(), 899, topk[k], (i + 1) / 10.0);
                    Arrays.sort(postionScoreDtos, new Comparator<PostionScoreDto>() {
                        @Override
                        public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                            return o1.getVenueId() - o2.getVenueId();
                        }
                    });
                    int ptj = 0;
                    int pti = 0, commen = 0;
                    while (pti < postionScoreDtos.length && ptj < userCheckInFreq.getCheckInpost().length) {
                        int d = postionScoreDtos[pti].getVenueId() - userCheckInFreq.getCheckInpost()[ptj];
                        if (d == 0) {
                            pti++;
                            ptj++;
                            commen++;
                        } else if (d > 0) {
                            ptj++;
                        } else {
                            pti++;
                        }
                    }
                    res[i][k][0] += commen / (double) topk[k];
                    res[i][k][1] += commen / (double) userCheckInFreq.getCheckInpost().length;
                }
            }
        }
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 3; j++) {
                res[i][j][0] /= 100.0;
                res[i][j][1] /= 100.0;
            }
        }
        return res;
    }

    public PostionScoreDto[] getWPRScore(int userId, int venueId, int topK, double r) {
        if (WPRCache[userId] == null) {
            WPRCache[userId] = userAlgorithm.PG_gorithmScore(userId, venueId, 15);
            Arrays.sort(WPRCache[userId], new Comparator<PostionScoreDto>() {
                @Override
                public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                    double d = o1.getScore() - o2.getScore();
                    return d == 0 ? 0 : d > 0 ? 1 : -1;
                }
            });
        }
        if (itemWPR == null) {
            itemWPR = itemAlgorithm.PG_gorithmScore(0, venueId, 15);
            Arrays.sort(itemWPR, new Comparator<PostionScoreDto>() {
                @Override
                public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                    double d = o1.getScore() - o2.getScore();
                    return d == 0 ? 0 : d > 0 ? 1 : -1;
                }
            });
        }
        userWPR = WPRCache[userId];

        HashMap<Integer, Double> tempHash = new HashMap<>();
        double s=0.0;
        for (int i = userWPR.length-1; i>=0&&i >=userWPR.length-topK; i--) {
            s+=userWPR[i].getScore();
        }
        for (int i = userWPR.length-1; i>=0&&i >=userWPR.length-topK; i--) {
            tempHash.put(userWPR[i].getVenueId(),tempHash.getOrDefault(userWPR[i].getVenueId(),0.0)+userWPR[i].getScore()*r/s);
        }
        s=0.0;
        for (int i = itemWPR.length-1; i>=0&&i >=itemWPR.length-topK; i--) {
            s+=itemWPR[i].getScore();
        }
        for (int i = itemWPR.length-1; i>=0&&i >=itemWPR.length-topK; i--) {
            tempHash.put(itemWPR[i].getVenueId(),tempHash.getOrDefault(itemWPR[i].getVenueId(),0.0)+itemWPR[i].getScore()*(1-r)/s);
        }
        ArrayList<PostionScoreDto> res = new ArrayList<>();
        for (Integer integer : tempHash.keySet()) {
            res.add(new PostionScoreDto(integer,tempHash.get(integer)));
        }
        res.sort(new Comparator<PostionScoreDto>() {
            @Override
            public int compare(PostionScoreDto o1, PostionScoreDto o2) {
                double d = o2.getScore() - o1.getScore();
                return d == 0 ? 0 : d > 0 ? 1 : -1;
            }
        });

        return res.subList(0, topK).toArray(new PostionScoreDto[0]);
    }

}
